package com.yf.system.modules.sys.user.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yf.ability.system.captcha.service.CaptchaService;
import com.yf.ability.system.shiro.dto.SysUserLoginDTO;
import com.yf.ability.system.shiro.jwt.JwtUtils;
import com.yf.framework.api.ApiError;
import com.yf.framework.api.dto.PagingReqDTO;
import com.yf.framework.enums.CommonState;
import com.yf.framework.exception.ServiceException;
import com.yf.framework.utils.BeanMapper;
import com.yf.framework.utils.passwd.PassHandler;
import com.yf.framework.utils.passwd.PassInfo;
import com.yf.ability.Constant;

import com.yf.ability.system.redis.service.RedisService;
import com.yf.ability.business.sms.service.SmsService;
import com.yf.system.CacheKey;
import com.yf.system.config.function.FunctionConfig;
import com.yf.system.config.function.enums.FunctionKey;
import com.yf.system.modules.sys.depart.service.SysDepartService;
import com.yf.system.modules.sys.user.dto.request.*;
import com.yf.system.modules.sys.user.dto.response.UserExportDTO;
import com.yf.system.modules.sys.user.dto.response.UserListRespDTO;
import com.yf.system.modules.sys.user.entity.SysRole;
import com.yf.system.modules.sys.user.entity.SysUser;
import com.yf.system.modules.sys.user.mapper.SysUserMapper;
import com.yf.system.modules.sys.user.service.SysUserRoleService;
import com.yf.system.modules.sys.user.utils.SignUtils;
import com.yf.system.modules.sys.user.utils.UserUtils;
import com.yf.system.modules.sys.user.service.SysUserService;

import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;

/**
 * 用户业务实现类
 * @author bool
 */
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

    @Autowired
    private FunctionConfig functionConfig;

    @Autowired
    private SysUserRoleService sysUserRoleService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private CaptchaService captchaService;

    @Autowired
    private SmsService smsService;

    @Autowired
    private SysDepartService sysDepartService;


    /**
     * 默认用户角色
     */
    private static final String ROLE_USER = "user";
    private static final String DEFAULT_PASS = "123456";


    @Override
    public IPage<UserListRespDTO> paging(PagingReqDTO<SysUserQueryReqDTO> reqDTO) {

        //创建分页对象
        Page page = reqDTO.toPage();

        //转换结果
        IPage<UserListRespDTO> pageData = baseMapper.paging(page, reqDTO.getParams());
        return pageData;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(List<String> ids) {
        this.removeByIds(ids);
        sysUserRoleService.removeByUserIds(ids);
    }

    @Override
    public SysUserLoginDTO login(SysUserLoginReqDTO reqDTO) {

        // 校验图形验证码
        boolean check = captchaService.checkCaptcha(reqDTO.getCaptchaKey(), reqDTO.getCaptchaValue());
        if (!check) {
            throw new ServiceException("图形验证码不正确或已失效！");
        }


        QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(SysUser::getUserName, reqDTO.getUsername());
        SysUser user = this.getOne(wrapper, false);

        // 校验用户状态&密码
        return this.checkAndLogin(user, reqDTO.getPassword());
    }


    /**
     * 用户登录校验
     *
     * @param user
     */
    private SysUserLoginDTO checkAndLogin(SysUser user, String password) {

        if (user == null) {
            throw new ServiceException(ApiError.ERROR_90010001);
        }

        // 被禁用
        if (user.getState().equals(CommonState.ABNORMAL)) {
            throw new ServiceException(ApiError.ERROR_90010005);
        }

        if (!StringUtils.isBlank(password)) {
            boolean pass = PassHandler.checkPass(password, user.getSalt(), user.getPassword());
            if (!pass) {
                throw new ServiceException(ApiError.ERROR_90010002);
            }
        }

        return this.setToken(user);
    }

    @Override
    public SysUserLoginDTO token(String token) {

        // 获得会话
        String username;
        try {
            username = JwtUtils.getUsername(token);
        } catch (Exception e) {
            throw new ServiceException("会话失效，请重新登录！");
        }

        JSONObject json = redisService.getJson(Constant.USER_NAME_KEY + username);
        if (json == null) {
            throw new ServiceException(ApiError.ERROR_10010002);
        }


        SysUserLoginDTO respDTO = json.toJavaObject(SysUserLoginDTO.class);

        // T下线的操作
        if (!token.equals(respDTO.getToken())) {
            throw new ServiceException("您的账号在其他地方登录了！");
        }


        // 填充积分信息
        SysUser user = this.getById(respDTO.getId());

        if (user == null) {
            // 可能是脏的用户数据
            throw new ServiceException(ApiError.ERROR_10010002);
        }


        respDTO.setPoints(user.getPoints());
        respDTO.setRealName(user.getRealName());
        respDTO.setIdCard(user.getIdCard());
        respDTO.setEmail(user.getEmail());
        respDTO.setMobile(user.getMobile());
        respDTO.setAvatar(user.getAvatar());
        return respDTO;
    }

    @CacheEvict(value = CacheKey.TOKEN, key = "#token")
    @Override
    public void logout(String token) {

        try {
            String username = JwtUtils.getUsername(token);
            String[] keys = new String[]{Constant.USER_NAME_KEY + username};
            redisService.del(keys);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void update(SysUserUpdateReqDTO reqDTO) {

        // 查找旧的用户信息
        SysUser u1 = this.getById(UserUtils.getUserId());


        SysUser user = new SysUser();
        user.setId(UserUtils.getUserId());
        BeanMapper.copy(reqDTO, user);

        // 修改标识
        boolean reLogin = false;

        // 修改密码
        String password = reqDTO.getPassword();
        if (!StringUtils.isBlank(password)) {
            PassInfo passInfo = PassHandler.buildPassword(password);
            user.setPassword(passInfo.getPassword());
            user.setSalt(passInfo.getSalt());
            reLogin = true;
        }

        // 重新登录
        if (reLogin) {
            // 退出登录
            String[] keys = new String[]{Constant.USER_NAME_KEY + user.getUserName()};
            redisService.del(keys);
        }

        this.updateById(user);
        this.setToken(user);
    }

    @Override
    public void pass(SysUserPassReqDTO reqDTO) {

        // 获取当前用户
        SysUser user = this.getById(UserUtils.getUserId());

        boolean check = PassHandler.checkPass(reqDTO.getOldPassword(), user.getSalt(), user.getPassword());
        if (!check) {
            throw new ServiceException(ApiError.ERROR_90010007);
        }

        PassInfo passInfo = PassHandler.buildPassword(reqDTO.getPassword());
        user.setPassword(passInfo.getPassword());
        user.setSalt(passInfo.getSalt());
        this.updateById(user);

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void save(SysUserSaveReqDTO reqDTO) {

        List<String> roles = reqDTO.getRoles();

        if (CollectionUtils.isEmpty(roles)) {
            throw new ServiceException(ApiError.ERROR_90010003);
        }

        QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(SysUser::getUserName, reqDTO.getUserName());
        if (!StringUtils.isBlank(reqDTO.getId())) {
            wrapper.lambda().ne(SysUser::getId, reqDTO.getId());
        }

        int count = this.count(wrapper);
        if (count > 0) {
            throw new ServiceException("用户名不能重复！");
        }


        // 保存基本信息
        SysUser user;

        // 添加模式
        if (StringUtils.isBlank(reqDTO.getId())) {
            user = new SysUser();
            BeanMapper.copy(reqDTO, user);
            user.setId(IdWorker.getIdStr());
        } else {
            user = this.getById(reqDTO.getId());
            BeanMapper.copy(reqDTO, user);
        }

        // 修改密码
        if (!StringUtils.isBlank(reqDTO.getPassword())) {
            PassInfo pass = PassHandler.buildPassword(reqDTO.getPassword());
            user.setPassword(pass.getPassword());
            user.setSalt(pass.getSalt());
        }

        // 保存角色信息
        sysUserRoleService.saveRoles(user.getId(), roles);
        this.saveOrUpdate(user);
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public SysUserLoginDTO reg(UserRegReqDTO reqDTO) {

        // 校验功能
        functionConfig.hasFunction(FunctionKey.REG);


        // 校验图形验证码
        boolean check = captchaService.checkCaptcha(reqDTO.getCaptchaKey(), reqDTO.getCaptchaValue());
        if (!check) {
            throw new ServiceException("图形验证码不正确或已失效！");
        }


        // 校验用户名
        QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(SysUser::getUserName, reqDTO.getUserName());
        int count = this.count(wrapper);
        if (count > 0) {
            throw new ServiceException("用户名已存在，换一个吧！");
        }

        // 保存并登录
        return this.saveAndLogin(
                null,
                reqDTO.getUserName(),
                null,
                reqDTO.getRealName(),
                ROLE_USER,
                null,
                "",
                reqDTO.getPassword());
    }


    /**
     * 保存用户并自动登录
     *
     * @param userName
     * @param deptCode
     * @param realName
     * @param mobile
     * @param avatar
     * @param password
     * @return
     */
    private SysUserLoginDTO saveAndLogin(String userId, String userName, String deptCode, String realName, String role, String mobile, String avatar, String password) {

        // 保存用户
        SysUser user = new SysUser();

        // 指定用户ID的
        if (!StringUtils.isBlank(userId)) {
            user.setId(userId);
        } else {
            user.setId(IdWorker.getIdStr());
        }

        user.setUserName(userName);
        user.setRealName(realName);
        user.setDeptCode(deptCode);
        user.setMobile(mobile);
        user.setAvatar(avatar);
        PassInfo passInfo = PassHandler.buildPassword(password);
        user.setPassword(passInfo.getPassword());
        user.setSalt(passInfo.getSalt());

        // 保存角色
        List<String> roleList = new ArrayList<>();
        if (!StringUtils.isBlank(role)) {
            roleList.add(role);
        } else {
            // 默认学员
            roleList.add(ROLE_USER);
        }


        // 保存角色
        sysUserRoleService.saveRoles(user.getId(), roleList);

        // 保存用户
        this.save(user);
        return this.setToken(user);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void importExcel(List<UserExportDTO> reqDTO) {
        for (UserExportDTO item : reqDTO) {
            this.importUser(item);
        }
    }

    @Override
    public List<UserExportDTO> listForExport(SysUserQueryReqDTO reqDTO) {
        return baseMapper.listForExport(reqDTO);
    }

    @Override
    public SysUserSaveReqDTO detail(String id) {

        // 基础信息复制
        SysUser user = this.getById(id);
        SysUserSaveReqDTO respDTO = new SysUserSaveReqDTO();
        BeanMapper.copy(user, respDTO);

        // 角色是要
        List<String> roles = sysUserRoleService.listRoleIds(user.getId());
        respDTO.setRoles(roles);
        return respDTO;
    }


    @Override
    public void batchDept(UserDeptReqDTO reqDTO) {
        QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
        wrapper.lambda().in(SysUser::getId, reqDTO.getUserIds());

        SysUser user = new SysUser();
        user.setDeptCode(reqDTO.getDeptCode());
        this.update(user, wrapper);
    }

    @Override
    public void resetPass(ResetPassReqDTO reqDTO) {

        // 校验短信
        boolean check = smsService.checkSms(reqDTO.getMobile(), reqDTO.getSmsCode());

        if (!check) {
            throw new ServiceException("短信验证码错误，请确认！");
        }

        QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(SysUser::getMobile, reqDTO.getMobile());
        SysUser user = this.getOne(wrapper, false);

        if (user == null) {
            throw new ServiceException("无法找到对应手机号的用户！");
        }

        PassInfo passInfo = PassHandler.buildPassword(reqDTO.getNewPassword());
        user.setPassword(passInfo.getPassword());
        user.setSalt(passInfo.getSalt());
        this.updateById(user);

    }





    /**
     * 导入用户
     *
     * @param reqDTO
     */
    private void importUser(UserExportDTO reqDTO) {

        QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(SysUser::getUserName, reqDTO.getUserName());

        int count = this.count(wrapper);

        if (count > 0) {
            throw new ServiceException("用户名【" + reqDTO.getUserName() + "】已存在，无法导入！");
        }


        // 保存用户
        SysUser user = new SysUser();
        BeanMapper.copy(reqDTO, user);
        user.setId(IdWorker.getIdStr());

        String pass = reqDTO.getPassword();
        if (StringUtils.isBlank(pass)) {
            pass = DEFAULT_PASS;
        }
        PassInfo passInfo = PassHandler.buildPassword(pass);
        user.setPassword(passInfo.getPassword());
        user.setSalt(passInfo.getSalt());

        // 保存角色
        List<String> roles = new ArrayList<>();
        if (StringUtils.isBlank(reqDTO.getRoleIds())) {
            roles.add(ROLE_USER);
        } else {
            // 角色列表
            String[] roleIds = reqDTO.getRoleIds().split(",");
            roles = Arrays.asList(roleIds);
        }

        sysUserRoleService.saveRoles(user.getId(), roles);
        this.save(user);
    }

    /**
     * 保存会话信息
     *
     * @param user
     * @return
     */
    private SysUserLoginDTO setToken(SysUser user) {

        // 获取一个用户登录的信息
        String key = Constant.USER_NAME_KEY + user.getUserName();
        String json = redisService.getString(key);
        if (!StringUtils.isBlank(json)) {
            // 删除旧的会话
            redisService.del(key);
        }

        SysUserLoginDTO respDTO = new SysUserLoginDTO();
        BeanMapper.copy(user, respDTO);

        // 根据用户生成Token
        String token = JwtUtils.sign(user.getUserName());
        respDTO.setToken(token);

        // 添加角色信息
        this.fillRoleData(respDTO);

        // 权限表，用于前端控制按钮
        List<String> permissions = sysUserRoleService.findUserPermission(user.getId());
        respDTO.setPermissions(permissions);


        // 保存如Redis
        redisService.set(key, JSON.toJSONString(respDTO));

        return respDTO;

    }


    /**
     * 追加用户角色信息
     *
     * @param respDTO
     */
    private void fillRoleData(SysUserLoginDTO respDTO) {

        // 角色是要
        List<SysRole> roleList = sysUserRoleService.listRoles(respDTO.getId());

        // 数据权限1最小：查看自己的数据
        Integer dataScope = 1;
        // 角色类型1最小：学生角色不能进后台
        Integer roleType = 1;
        List<String> roleIds = new ArrayList<>();
        for (SysRole role : roleList) {
            // 角色ID
            roleIds.add(role.getId());
            // 替换大的权限
            if (dataScope < role.getDataScope()) {
                dataScope = role.getDataScope();
            }
            // 替换大的角色类型
            if (roleType < role.getRoleType()) {
                roleType = role.getRoleType();
            }
        }

        respDTO.setRoleType(roleType);
        respDTO.setDataScope(dataScope);
        respDTO.setRoles(roleIds);
    }

    @Override
    public SysUserLoginDTO syncLogin(String userId, String userName, String realName, String role, Long timestamp, String departs, String token) {


        // 30秒有效
        Calendar cl = Calendar.getInstance();
        cl.setTimeInMillis(timestamp * 1000L);
        cl.add(Calendar.SECOND, 30);

        if (cl.getTime().before(new Date())) {
            throw new ServiceException("token时效不正确！");
        }

        // 签名校验
        boolean check = SignUtils.checkToken(userName, timestamp, token);
        if (!check) {
            throw new ServiceException("签名验证失败！");
        }

        SysUser user;

        // 有ID
        if (!StringUtils.isBlank(userId)) {
            user = this.getById(userId);
        } else {
            QueryWrapper<SysUser> wrapper = new QueryWrapper<>();
            wrapper.lambda().eq(SysUser::getUserName, userName);
            user = this.getOne(wrapper, false);
        }

        // 检查用户并登录
        if (user != null) {
            return this.checkAndLogin(user, null);
        } else {
            String deptCode = sysDepartService.syncDepart(departs);
            return this.saveAndLogin(userId, userName, deptCode, realName, role, "", "", RandomStringUtils.randomAlphanumeric(16));
        }
    }
}
